package com.gitee.aurora.core.comm;

import com.gitee.aurora.core.utils.DBUtils;
import com.gitee.aurora.core.utils.MapToObj;
import org.apache.commons.collections.map.CaseInsensitiveMap;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Component
public class AuroraJdbcTemplate extends JdbcTemplate {

    public AuroraJdbcTemplate(DataSource dataSource) {
        super(dataSource);
    }

    /**
     * @param sql         sql
     * @param elementType elementType
     * @param <T>         返回值类型
     * @return 返回值
     * @throws Exception exception
     */
    public <T> List<T> queryForMapToList(String sql, Class<T> elementType) {
        List<T> arrayList = new ArrayList<>();
        List<Map<String, Object>> maps = super.queryForList(sql);
        for (Map<String, Object> m : maps) {
            T t = MapToObj.mapToObj(m, elementType);
            arrayList.add(t);
        }
        return arrayList;
    }


    public <T> List<T> queryForMapToList(String sql, Object[] objects, int[] types, Class<T> elementType) {
        List<T> arrayList = new ArrayList<>();
        List<Map<String, Object>> maps = super.queryForList(sql, objects, types);
        for (Map<String, Object> m : maps) {
            T t = MapToObj.mapToObj(m, elementType);
            arrayList.add(t);
        }
        return arrayList;
    }

    /**
     * 过时的方法，建议使用 根据数据源获取列名插入 public <T> int[] batchInsertForTableName(DataSource dataSource, String tableName, List<T> insertData)
     * @param tableName
     * @param list
     * @param elementType
     * @param excludeField
     * @return
     * @param <T>
     * @throws Exception
     */
    @Deprecated
    public <T> int[] batchInsertForTableName(String tableName, List<T> list, Class<T> elementType, String excludeField) throws IllegalAccessException {

        Field[] fieldList = elementType.getDeclaredFields();
        List<String> strings = new ArrayList<>();
        List<String> fieldNameList = new ArrayList<>();
        StringBuilder stringBuffer = new StringBuilder("INSERT INTO ");
        for (int x = 0; x < fieldList.length; x++) {
            strings.add("?");
        }
        String collect = strings.stream().map(String::valueOf).collect(Collectors.joining(","));

        List<Object[]> param = new ArrayList<>();
        for (T t : list) {
            List<Object> fieldValueList = new ArrayList<>();
            for (Field field : elementType.getDeclaredFields()) {
                String fieldName = field.getName();
                if (fieldNameList.size() < fieldList.length) {
                    fieldNameList.add(fieldName);
                }
                field.setAccessible(true);
                Object value = field.get(t);
                fieldValueList.add(value);
            }
            Object[] objects = fieldValueList.toArray();
            param.add(objects);
        }
        String fieldNameStr = fieldNameList.stream().map(String::valueOf).collect(Collectors.joining(","));
        fieldNameStr = fieldNameStr.replace(excludeField, "S_ID");
        stringBuffer.append(tableName).append("(").append(fieldNameStr).append(")").append("VALUES").append("(").append(collect).append(")");

        return this.batchUpdate(stringBuffer.toString(), param);
    }

    /**
     * 根据数据库插入更新
     *
     * @param dataSource 数据源
     * @param tableName  表名
     * @param list       数据
     * @param <T>        class
     * @return 返回插入的结果
     * @throws Exception 异常
     */
    public <T> void batchInsertOrUpdateForTableName(DataSource dataSource, String tableName, List<T> insertData,List<T> updateData) throws Exception {

        batchInsertForTableName(dataSource, tableName, insertData);
        batchUpdateForTableName(dataSource, tableName, updateData);
    }

    public <T> int[] batchInsertForTableName(DataSource dataSource, String tableName, List<T> insertData) throws SQLException {
        DBUtils dbUtils = new DBUtils(dataSource);
        List<String> dbFieldNameList = dbUtils.getDBFieldNameList(tableName);

        List<String> strings = new ArrayList<>();
        StringBuilder stringBuffer = new StringBuilder("INSERT INTO ");

        for (String ignored : dbFieldNameList) {
            strings.add("?");
        }
        String collect = strings.stream().map(String::valueOf).collect(Collectors.joining(","));

        List<Object[]> param = new ArrayList<>();
        for (T t : insertData) {
            List<Object> fieldValueList = new ArrayList<>();
            CaseInsensitiveMap beanMap = new CaseInsensitiveMap(Objects.requireNonNull(MapToObj.ObjToMap(t)));
            for (String fieldName : dbFieldNameList) {
                Object value;
                if ("S_ID".equals(fieldName)) {
                    value = beanMap.get("sId");
                    fieldValueList.add(value);
                }else if(beanMap.containsKey(fieldName)){
                    value = beanMap.get(fieldName);
                    fieldValueList.add(value);
                }else {
                    fieldValueList.add(null);
                }
            }
            Object[] objects = fieldValueList.toArray();
            param.add(objects);
        }
        String fieldNameStr = dbFieldNameList.stream().map(String::valueOf).collect(Collectors.joining("`,`"));
        stringBuffer.append(tableName).append("(").append("`").append(fieldNameStr).append("`").append(")").append("VALUES").append("(").append(collect).append(")");

        return this.batchUpdate(stringBuffer.toString(), param);
    }

    public <T> int[] batchUpdateForTableName(DataSource dataSource, String tableName, List<T> updateData) throws SQLException {
        DBUtils dbUtils = new DBUtils(dataSource);
        List<String> dbFieldNameList = dbUtils.getDBFieldNameList(tableName);
        List<String> strings = new ArrayList<>();
        StringBuilder stringBuffer = new StringBuilder("UPDATE "+tableName+" SET ");

        for (String s : dbFieldNameList) {
            strings.add("`"+s+"`=?");
        }
        String collect = strings.stream().map(String::valueOf).collect(Collectors.joining(","));

        List<Object[]> param = new ArrayList<>();
        for (T t : updateData) {
            List<Object> fieldValueList = new ArrayList<>();
            CaseInsensitiveMap beanMap = new CaseInsensitiveMap(Objects.requireNonNull(MapToObj.ObjToMap(t)));
            for (String fieldName : dbFieldNameList) {
                Object value;
                // 更新时要先排除S_ID,后面再为S_ID赋值
                if (!"S_ID".equals(fieldName)) {
                    value = beanMap.get(fieldName);
                    fieldValueList.add(value);
                }
            }
            fieldValueList.add(beanMap.get("sId"));
            Object[] objects = fieldValueList.toArray();
            param.add(objects);
        }
        stringBuffer.append(collect).append(" WHERE `S_ID`=?");

        return this.batchUpdate(stringBuffer.toString(), param);
    }


    public <T> int[] batchUpdateForTableName(String tableName, List<T> list, Class<T> elementType) throws IllegalAccessException {

        Field[] fieldList = elementType.getDeclaredFields();
        List<String> strings = new ArrayList<>();
        List<String> fieldNameList = new ArrayList<>();
        StringBuilder stringBuffer = new StringBuilder("INSERT INTO ");
        for (int x = 0; x < fieldList.length; x++) {
            strings.add("?");
        }
        String collect = strings.stream().map(String::valueOf).collect(Collectors.joining(","));

        List<Object[]> param = new ArrayList<>();
        for (T t : list) {
            List<Object> fieldValueList = new ArrayList<>();
            for (Field field : elementType.getDeclaredFields()) {
                String fieldName = field.getName();
                if (fieldNameList.size() < fieldList.length) {
                    fieldNameList.add(fieldName);
                }
                field.setAccessible(true);
                Object value = field.get(t);
                fieldValueList.add(value);
            }
            Object[] objects = fieldValueList.toArray();
            param.add(objects);
        }
        String fieldNameStr = fieldNameList.stream().map(String::valueOf).collect(Collectors.joining(","));
        stringBuffer.append(tableName).append("(").append(fieldNameStr).append(")").append("VALUES").append("(").append(collect).append(")");

        return this.batchUpdate(stringBuffer.toString(), param);
    }
}
